This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

library(ape)
library(TreeTools)
library(phangorn)
library(randomcoloR)
library(ggplot2)
library(dplyr)
library(uniqtag)
library(phytools)
library(ggstance)
library(ggtree)
library(grid)
library(gtable)
library(reshape2)
library(ggnewscale)
library(htmlwidgets)

Download global overview tab

raw.data<-read.csv("../../misc/data/GAGA_SPEC_TUBE_ECOL-GlobalOverview-210322.tsv",sep="\t",strip.white = T,stringsAsFactors = F,dec = ",",na.strings = c("","#DIV/0!"),skip = 2)

Load from google

# subset to entries with a Species name from the Assembly Overview
data<-subset(GlobalOverview,!is.na(Species..from.Assembly.Overview.) & !is.na(ENTRY.FROM.ASSEMBLY.OVERVIEW))

# rename Myrmoxenus to Temnothorax as the former is no proper genus
data$Species..from.Assembly.Overview.<-gsub("Myrmoxenus","Temnothorax",data$Species..from.Assembly.Overview.)

# Extract genus information 
data$genus<-gsub(" .*","",data$Species..from.Assembly.Overview.)

# Rename duplicate genera to Genus-sp1, Genus-sp2, etc.
data$taxID<-make_unique(data$genus,sep="-sp")

# show which genera are duplicated
table(gsub("-.*","",data$genus[duplicated(gsub(" .*","",data$Species..from.Assembly.Overview.))]))+1

   Acromyrmex Aphaenogaster          Atta    Camponotus      Carebara Crematogaster      Diacamma  Ectomomyrmex       Formica  Harpegnathos        Lasius 
            5             3             2             6             7             3             2             2             6             2             8 
   Leptogenys        Messor      Myrmecia       Myrmica  Odontomachus      Pheidole   Proceratium    Proformica   Rossomyrmex    Solenopsis      Tapinoma 
            3             2             3             3             3             5             2             2             2             2             2 
  Temnothorax   Tetramorium  Trachymyrmex 
            8             5             3 
# Downloaded ant tree from https://www.antwiki.org/wiki/Phylogeny_of_Formicidae
## The tree contains a few typos that I corrected manually (e.g. Paratrachina instead of Paratrechina, Myrmecosystus instead of Myrmecocystus)
treePaste<-paste(scan("../../misc/data/antTree.tre", what="character", sep=" "),collapse=" ")
Read 1 item
# expand the nodes for those species that we have multiple times
expansions<-table(data$genus[duplicated(data$genus)])+1
for (i in 1:length(expansions)){
  treePaste<-gsub(paste("(",names(expansions)[i],":)",sep=""),paste("(",paste(rep("\\1",expansions[i]),collapse=":0,",sep=""),":0):",sep=""),treePaste)
}

# load manipulated tree as tree object
raw.tree<-read.tree(text=treePaste)
raw.tree$tip.label<-make_unique(raw.tree$tip.label,sep="-sp")
# show labels for figuring out subfamilies
plot(raw.tree,show.tip.label = F)
92 branch length(s) NA(s): branch lengths ignored in the plot
nodelabels(frame =  "none")

# subset to extant ants, Martialis has to be node 1
tree<-Subtree(Preorder(raw.tree), 427)
grep("Martialis",tree$tip.label)
[1] 1
tree$tip.label<-gsub("some(.*[2-9])|some(.*)1","\\1\\2",tree$tip.label)
# plot with subfamilies

p<-ggtree(tree,size=.1)+xlim(0,40)+geom_tiplab(align=T,size=1)+ geom_text(aes(label=node),size=1,col="darkred")
Duplicated aesthetics after name standardisation: size
# Download species counts for all genera and other info from antwiki
#curl https://www.antwiki.org/wiki/images/a/ad/AntWiki_Valid_Genera.txt | LC_ALL=C sed 's/[^[:blank:][:print:]]//g'  > ../misc/data/AntWiki.clean.tsv
speciescount<-read.csv("../../misc/data/AntWiki.clean.tsv",sep="\t")
speciescount$Tribe[speciescount$Tribe==""]<-paste("tr. ",speciescount$Subfamily[speciescount$Tribe==""],sep="")
dataMerge<-merge(data,speciescount,by.x="genus",by.y="TaxonName",all.x=T,all.y=T)

dataMerge <- dataMerge %>%
  select(taxID, everything())

# give name to all unsequenced genera
dataMerge$taxID[is.na(dataMerge$taxID)]<-dataMerge$genus[is.na(dataMerge$taxID)]
# set species Count to NA for all multiple genera species
dataMerge$SpeciesCount[grep("-sp[2-9]",dataMerge$taxID,value = F)]<-NA

add data to tree

p<-ggtree(tree,size=.2,layout = "circular")+xlim(0,40)
#p$data$label<-make_unique(p$data$label,sep = "-sp")
#p$data$label[grep("NA.",p$data$label,ignore.case = F)]<-NA
dataMerge$DisplayName<-gsub(" \\(.*","",dataMerge$Species..from.Assembly.Overview.)
p2<-p %<+% dataMerge 

antGenera.tree <- p2 +
          geom_tiplab(align=T,size=1.2,offset=.5,linesize=0)+
          geom_tippoint(aes(size = SpeciesCount,fill=Tribe),pch=21,alpha=.5) + 
          guides(fill="none",color="none")+
          theme_tree()+
          theme(legend.position = c(.9,.85),legend.background = element_blank())
Duplicated aesthetics after name standardisation: sizeDuplicated aesthetics after name standardisation: size
antGenera.tree.w.GAGA <- p2 +
          geom_tiplab(aes(label=DisplayName,col=Tribe,alpha=ifelse(is.na(DisplayName),0,1)),align=T,size=1.8,offset = 8,linesize = 0)+
          geom_tiplab(align=T,size=1.2,offset=.5,linesize=0)+
          geom_tippoint(aes(size = SpeciesCount,fill=Tribe),pch=21,alpha=.5) + 
          guides(fill="none",color="none",alpha="none")+
          theme_tree()+
          theme(legend.position = c(.9,.85),legend.background = element_blank())
Duplicated aesthetics after name standardisation: sizeDuplicated aesthetics after name standardisation: sizeDuplicated aesthetics after name standardisation: sizeDuplicated aesthetics after name standardisation: size
antGenera.tree
ggsave("../../misc/plots/antTree.pdf",height=10)
Saving 7.29 x 10 in image

antGenera.tree.w.GAGA
ggsave("../../misc/plots/antTree.GAGA.pdf",height=10)
Saving 7.29 x 10 in image

# How many GAGA species are shown?
sum(!is.na(dataMerge$DisplayName))
[1] 163
GAGA.tree<-keep.tip(tree,antGenera.tree.w.GAGA$data$node[!is.na(antGenera.tree.w.GAGA$data$DisplayName)])

GAGA.tree.plot<-ggtree(GAGA.tree,size=.2) %<+% dataMerge 
GAGA.tree.plot2<-GAGA.tree.plot+geom_tiplab(aes(label=DisplayName,col=ifelse(grepl("GAGA",ENTRY.FROM.ASSEMBLY.OVERVIEW),"GAGA","other")),size=1.5,show.legend=F)+scale_color_manual(name="",values = c("black","grey60"))
GAGA.tree.plot2

GAGA.tree.plot3

# extract data from tree for facet plotting

facetData<-as.data.frame(GAGA.tree.plot2$data[GAGA.tree.plot2$data$isTip==T,])
facetData$id<-facetData$label
facetData$Genome.size<-as.numeric(facetData$Genome.size)
facetData$Scaffold.N50<-as.numeric(facetData$Scaffold.N50)
facetData$Contig.N50<-as.numeric(facetData$Contig.N50)
facetData$Number.of.scaffolds<-as.numeric(facetData$Number.of.scaffolds)

# extract BUSCO scores
facetData$BUSCO.hym.C<-as.numeric(gsub("C:(.*?)%.*","\\1",facetData$BUSCO.Hymenoptera))
facetData$BUSCO.hym.F<-as.numeric(gsub(".*F:(.*?)%.*","\\1",facetData$BUSCO.Hymenoptera))
facetData$BUSCO.hym.M<-as.numeric(gsub(".*M:(.*?)%.*","\\1",facetData$BUSCO.Hymenoptera))

facetData$BUSCO.euk.C<-as.numeric(gsub("C:(.*?)%.*","\\1",facetData$BUSCO.Eukaryota))
facetData$BUSCO.euk.F<-as.numeric(gsub(".*F:(.*?)%.*","\\1",facetData$BUSCO.Eukaryota))
facetData$BUSCO.euk.M<-as.numeric(gsub(".*M:(.*?)%.*","\\1",facetData$BUSCO.Eukaryota))


dd <- data.frame(id=facetData$label, GS=round(facetData$Genome.size/1000000),N50=facetData$Scaffold.N50,nscf=facetData$Number.of.scaffolds,B.hym.C=facetData$BUSCO.hym.C,B.hym.F=facetData$BUSCO.hym.F,B.hym.M=facetData$BUSCO.hym.M,B.euk.C=facetData$BUSCO.euk.C,B.euk.F=facetData$BUSCO.euk.F,B.euk.M=facetData$BUSCO.euk.M,tribe=facetData$Tribe,PacBio=facetData$PacBio.1,stLFR=facetData$stLFR.2,HiC=facetData$Hi.C.1,RNAseq=facetData$RNA..additional.samples.still.under.seq..,Finalized=facetData$Current.state,wgs=facetData$Short.reads..WGS..1,Gid=facetData$GAGA.ID,status=facetData$Current.state)
dd$tribeColor <- cladeColor[match(dd$tribe, names(cladeColor))]

dd$PacBio[!is.na(dd$PacBio)]<-1
dd$PacBio[is.na(dd$PacBio)]<-0
dd$stLFR[!is.na(dd$stLFR)]<-1
dd$stLFR[is.na(dd$stLFR)]<-0
dd$HiC[!is.na(dd$HiC)]<-1
dd$HiC[is.na(dd$HiC)]<-0
dd$wgs[!is.na(dd$wgs)]<-1
dd$wgs[is.na(dd$wgs)]<-0

rownames(dd)<-dd$id

dd$RNAseq[!is.na(dd$RNAseq)]<-3
dd$RNAseq[is.na(dd$RNAseq)]<-0

dd$Finalized[grep("Final",dd$Finalized)]<-2
dd$Finalized[dd$Finalized!=2]<-0

dd$DisplayName<-facetData$DisplayName

dd$node<-facetData$node
dd$tech<-as.factor(paste(dd$PacBio,dd$HiC,dd$stLFR,sep=""))
levels(dd$tech)[levels(dd$tech)=="000"]<-"non-GAGA"
levels(dd$tech)[levels(dd$tech)=="100"]<-"PacBio"
levels(dd$tech)[levels(dd$tech)=="001"]<-"stLFR"
levels(dd$tech)[levels(dd$tech)=="101"]<-"P/s"
levels(dd$tech)[levels(dd$tech)=="110"]<-"P/H"
levels(dd$tech)[levels(dd$tech)=="111"]<-"P/H/s"


ddHeatmap<-dd[c("id","tribeColor","tribe","PacBio","stLFR","HiC","wgs","Finalized","RNAseq")]

#GAGA.tree.plot4<-facet_plot(GAGA.tree.plot3, 'Genome Size', data = dd, geom=geom_barh, mapping = aes(x = GS,fill=tribeColor), 
 #               stat='identity' )+ theme_tree2() + scale_fill_identity()
GAGA.tree.plot.heatmap<-gheatmap(GAGA.tree.plot3+ylim(0,165), ddHeatmap[,c(4:9)], offset=7, colnames=T, legend_title="Tech",  width = .25,colnames_position = "top", colnames_offset_y = 1,colnames_angle=65,hjust=0,font.size = 2.8)+
                  scale_fill_manual(values = c("grey90","lightgreen","darkred","lightblue"))+
                  theme(legend.position = "none")
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
GAGA.tree.plot.heatmap
ggsave("../../misc/plots/GAGA.tree.technique.pdf",GAGA.tree.plot.heatmap,width=6,height=10)

ddHeatmap2<-dd[c("id","tribeColor","tribe","B.hym.C","B.euk.C")]
colnames(ddHeatmap2)[4:5]<-c("Hymenopt.","Eukaryota")
row.names(ddHeatmap2)<-ddHeatmap2$id
heatcol <- c("steelblue","cyan", "red")
GAGA.tree.plot.busco<-gheatmap(GAGA.tree.plot3+ylim(0,165), ddHeatmap2[,c(4:5)], offset=7, colnames=T, legend_title="Tech",  width = .215,colnames_position = "top", colnames_offset_y = 1,colnames_angle=65,hjust=0,font.size = 2.5)+scale_fill_gradientn(colours = rev(heatcol),na.value = "grey90",name="% comp. Busco")+theme(legend.position = c(.2,.8),legend.background = element_blank())
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
GAGA.tree.plot.busco
ggsave("../../misc/plots/GAGA.tree.busco.pdf",GAGA.tree.plot.busco,width=6,height=10)


GAGA.tree.plot.N50<-facet_plot(GAGA.tree.plot.heatmap+new_scale("color"), 'scfN50 (Mb)', data = dd, geom=geom_point, mapping = aes(x = N50/1000000,color=tech),size=2)+ 
  theme_tree2()+
  scale_color_brewer(palette = "Set2" ,guide = guide_legend(nrow=2,label.position = "top"))+
  theme(legend.position = c(.9,.9),
        legend.background = element_rect(color="black",fill=rgb(0,0,0,0)),
        legend.margin = margin(0,2,2,2,"mm"),
        legend.spacing = unit(0,"mm"),
        legend.title = element_blank(),
        legend.key.height = unit(0,"mm"),
        legend.key.width = unit(0,"mm"),
        legend.box.spacing = unit(0,"mm"))+
        guides(fill="none")+
  ylim(0,170)
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
GAGA.tree.plot.N50

# change rel_width of panels
## see https://groups.google.com/g/bioc-ggtree/c/tZ0qkluBeGU/m/nkfWC9ixCwAJ

gt = ggplot_gtable(ggplot_build(GAGA.tree.plot.N50))
Removed 26 rows containing missing values (geom_point).
#gtable_show_layout(gt) # will show you the layout - very handy function
#gt # see plot layout in table format
gt$layout$l[grep('panel-1', gt$layout$name)] # you want to find the column specific to panel-2
[1] 5 7
gt$widths[5] = 1.9*gt$widths[5] # in this case it was colmun 7 - reduce the width by a half
#grid.draw(gt) # plot with grid draw
grid.draw(gt)
ggsave("../../misc/plots/GAGA.tree.n50.pdf",grid.draw(gt),width=10,height=9)
ggsave("../../misc/plots/GAGA.assemblyQuality.pdf",assembly.quality.plot2,width=6,height=6)
ggsave("../../misc/plots/GAGA.assemblyQuality.boxplots.pdf",GAGA.quality.boxplots,width=6,height=6)
dd$Subfamily<-facetData$Subfamily.y
dd$DisplayNameU<-make_unique(dd$DisplayName,sep="")
dd$DisplayNameU<- factor(dd$DisplayNameU, levels = dd$DisplayNameU[rev(order(dd$Subfamily,dd$GS))])
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgpgYGB7cn0KbGlicmFyeShhcGUpCmxpYnJhcnkoVHJlZVRvb2xzKQpsaWJyYXJ5KHBoYW5nb3JuKQpsaWJyYXJ5KHJhbmRvbWNvbG9SKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodW5pcXRhZykKbGlicmFyeShwaHl0b29scykKbGlicmFyeShnZ3N0YW5jZSkKbGlicmFyeShnZ3RyZWUpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShndGFibGUpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoZ2duZXdzY2FsZSkKbGlicmFyeShodG1sd2lkZ2V0cykKYGBgCgoKRG93bmxvYWQgZ2xvYmFsIG92ZXJ2aWV3IHRhYgpgYGB7cn0KI3Jhdy5kYXRhPC1yZWFkLmNzdigiLi4vLi4vbWlzYy9kYXRhL0dBR0FfU1BFQ19UVUJFX0VDT0wtR2xvYmFsT3ZlcnZpZXctMjEwMzIyLnRzdiIsc2VwPSJcdCIsc3RyaXAud2hpdGUgPSBULHN0cmluZ3NBc0ZhY3RvcnMgPSBGLGRlYyA9ICIsIixuYS5zdHJpbmdzID0gYygiIiwiI0RJVi8wISIpLHNraXAgPSAyKQoKYGBgCgpMb2FkIGZyb20gZ29vZ2xlCmBgYHtSIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiNpZDwtIjFlN0hqX0JROHJrZTVYVzdqOTNZTDZRVkFHYmN2c2IyeXc3b0Q4NlduUEpZIgojZ3NoZWV0PC1yZWFkX3NoZWV0KGlkLHNoZWV0PSJHbG9iYWxPdmVydmlldyIsc2tpcCA9IDIsbmEgPSBjKCIiLCIgIiwiTkEiLCIjRElWLzAhIiksY29sX3R5cGVzID0gImMiLHRyaW1fd3M9VCkKI0dsb2JhbE92ZXJ2aWV3PC1hcy5kYXRhLmZyYW1lKGdzaGVldCxzdHJpbmdzQXNGYWN0b3JzPUYpCgoKI2NvbG5hbWVzKEdsb2JhbE92ZXJ2aWV3KTwtYygiUk5Bc2VxSUQiLCJSTkEuQ29tbWVudCIsIlJOQS5Xb3JrZXIiLCJSTkEuTGFyZ2Ugd29ya2VyIiwiUk5BLlNtYWxsd29ya2VyIiwiUk5BLlNvbGRpZXIiLCJSTkEuR3luZSIsIlJOQS5RdWVlbiIsIlJOQS5NYWxlIiwiUk5BLkxhcnZhZSIsIlJOQS5QdXBhZSIsIlJOQS5Ccm9vZCIsIlJOQS5NaXhlZCIsIkVOVFJZLkZST00uU3BlY2llcy5JRC5wb2xpc2hlZCIsIm9yaWdpbmFsLnNwZWNpZXMuSUQiLCJmaW5hbC5zcGVjaWVzLklEIiwiRU5UUlkuRlJPTS5TYW1wbGVUeXBlIiwiUGFjQklvIiwic3RMRlIiLCJIaUMiLCJDb2xsZWN0b3IuRmlyc3RuYW1lIiwiQ29sbGVjdG9yLk5hbWUiLCJTYW1wbGUuSUQiLCJTcGVjaWVzIiwiUGFjQmlvLmxpYnJhcnkiLCJzdExGUi5saWJyYXJ5IiwiU2hvcnQucmVhZHMuLldHUy4ubGlicmFyeSIsIlJOQXNlcS5saWJyYXJ5IiwiUGFjQmlvIiwic3RMRlIuMSIsIlNob3J0LnJlYWRzLi5XR1MuIiwiSGkuQyIsIlJOQXNlcSIsIlN1YmZhbWlseSIsIkNvdW50cnkiLCJDb21tZW50IiwiRU5UUlkuRlJPTS5HQUdBLlNVQk1JU1NJT04iLCJHQUdBLklEIiwiRU5UUlkuRlJPTS5BU1NFTUJMWS5PVkVSVklFVyIsIlNwZWNpZXMuLmZyb20uQXNzZW1ibHkuT3ZlcnZpZXcuIiwiUGFjQmlvLjEiLCJzdExGUi4yIiwiU2hvcnQucmVhZHMuLldHUy4uMSIsIkhpLkMuMSIsIlJOQS4uYWRkaXRpb25hbC5zYW1wbGVzLnN0aWxsLnVuZGVyLnNlcS4uIiwiUGFjQmlvLmFzc2VtYmx5IiwiUGFjQmlvLnN0TEZSLldHUy5wb2xpc2hlZC5hc3NlbWJseSIsIlBhY0Jpby5zdExGUi5zY2FmZm9sZGVkIiwiUGFjQmlvLkhpLkMuc2NhZmZvbGRlZCIsInN0TEZSLmFzc2VtYmx5IiwiQ3VycmVudC5zdGF0ZSIsIkdlbm9tZS5zaXplIiwiTnVtYmVyLm9mLnNjYWZmb2xkcyIsIlNjYWZmb2xkLk41MCIsIkNvbnRpZy5ONTAiLCJMb25nZXN0LnNjYWZmb2xkIiwiQlVTQ08uRXVrYXJ5b3RhIiwiQlVTQ08uSHltZW5vcHRlcmEiLCJEdXBsaWNhdGVkLnNjYWZmb2xkcyIsIlRvdGFsLmxlbmd0aC5vZi5kdXBsaWNhdGVkLnNjYWZmb2xkcyIsIlBlcmNlbnRhZ2Uub2YuZHVwbGljYXRpb24iLCJDb21tZW50cyIsIkJhY3RlcmlhbC5zY2FmZm9sZHMuY291bnQiLCJUb3RhbC5sZW5ndGgub2YuYmFjdGVyaWFsLnNjYWZmb2xkcyIsIkJhY3RlcmlhbC5zY2FmZm9sZC5wcmV2YWxlbmNlLi4iLCJUb3AuYmFjdGVyaWFsLnRheG9uIiwiUHV0YXRpdmUubWlzYXNzZW1ibHkuY291bnQiLCJQdXRhdGl2ZS5taXNhc3NlbWJsaWVzLi4uMTAwS2IuLmNvdW50IiwiR2Vub21lLnNpemUuMSIsIk51bWJlci5vZi5zY2FmZm9sZHMuMSIsIlNjYWZmb2xkLk41MC4xIiwiQ29udGlnLk41MC4xIiwiTG9uZ2VzdC5zY2FmZm9sZC4xIiwiQlVTQ08uRXVrYXJ5b3RhLjEiLCJCVVNDTy5IeW1lbm9wdGVyYS4xIiwiUmVwZWF0LmFubm90YXRpb24iLCJHZW5lLmFubm90YXRpb24iKQoKI3NhdmUoZmlsZSA9ICIuLi8uLi9taXNjL2RhdGEvZ2xvYmFsb3ZlcnZpZXcuUm9iamVjdCIsR2xvYmFsT3ZlcnZpZXcpCmxvYWQoZmlsZSA9ICIuLi8uLi9taXNjL2RhdGEvZ2xvYmFsb3ZlcnZpZXcuUm9iamVjdCIpCmBgYAoKYGBge3J9CiMgc3Vic2V0IHRvIGVudHJpZXMgd2l0aCBhIFNwZWNpZXMgbmFtZSBmcm9tIHRoZSBBc3NlbWJseSBPdmVydmlldwpkYXRhPC1zdWJzZXQoR2xvYmFsT3ZlcnZpZXcsIWlzLm5hKFNwZWNpZXMuLmZyb20uQXNzZW1ibHkuT3ZlcnZpZXcuKSAmICFpcy5uYShFTlRSWS5GUk9NLkFTU0VNQkxZLk9WRVJWSUVXKSkKCiMgcmVuYW1lIE15cm1veGVudXMgdG8gVGVtbm90aG9yYXggYXMgdGhlIGZvcm1lciBpcyBubyBwcm9wZXIgZ2VudXMKZGF0YSRTcGVjaWVzLi5mcm9tLkFzc2VtYmx5Lk92ZXJ2aWV3LjwtZ3N1YigiTXlybW94ZW51cyIsIlRlbW5vdGhvcmF4IixkYXRhJFNwZWNpZXMuLmZyb20uQXNzZW1ibHkuT3ZlcnZpZXcuKQoKIyBFeHRyYWN0IGdlbnVzIGluZm9ybWF0aW9uIApkYXRhJGdlbnVzPC1nc3ViKCIgLioiLCIiLGRhdGEkU3BlY2llcy4uZnJvbS5Bc3NlbWJseS5PdmVydmlldy4pCgojIFJlbmFtZSBkdXBsaWNhdGUgZ2VuZXJhIHRvIEdlbnVzLXNwMSwgR2VudXMtc3AyLCBldGMuCmRhdGEkdGF4SUQ8LW1ha2VfdW5pcXVlKGRhdGEkZ2VudXMsc2VwPSItc3AiKQoKIyBzaG93IHdoaWNoIGdlbmVyYSBhcmUgZHVwbGljYXRlZAp0YWJsZShnc3ViKCItLioiLCIiLGRhdGEkZ2VudXNbZHVwbGljYXRlZChnc3ViKCIgLioiLCIiLGRhdGEkU3BlY2llcy4uZnJvbS5Bc3NlbWJseS5PdmVydmlldy4pKV0pKSsxCgpgYGAKCgpgYGB7cn0KIyBEb3dubG9hZGVkIGFudCB0cmVlIGZyb20gaHR0cHM6Ly93d3cuYW50d2lraS5vcmcvd2lraS9QaHlsb2dlbnlfb2ZfRm9ybWljaWRhZQojIyBUaGUgdHJlZSBjb250YWlucyBhIGZldyB0eXBvcyB0aGF0IEkgY29ycmVjdGVkIG1hbnVhbGx5IChlLmcuIFBhcmF0cmFjaGluYSBpbnN0ZWFkIG9mIFBhcmF0cmVjaGluYSwgTXlybWVjb3N5c3R1cyBpbnN0ZWFkIG9mIE15cm1lY29jeXN0dXMpCnRyZWVQYXN0ZTwtcGFzdGUoc2NhbigiLi4vLi4vbWlzYy9kYXRhL2FudFRyZWUudHJlIiwgd2hhdD0iY2hhcmFjdGVyIiwgc2VwPSIgIiksY29sbGFwc2U9IiAiKQoKIyBleHBhbmQgdGhlIG5vZGVzIGZvciB0aG9zZSBzcGVjaWVzIHRoYXQgd2UgaGF2ZSBtdWx0aXBsZSB0aW1lcwpleHBhbnNpb25zPC10YWJsZShkYXRhJGdlbnVzW2R1cGxpY2F0ZWQoZGF0YSRnZW51cyldKSsxCmZvciAoaSBpbiAxOmxlbmd0aChleHBhbnNpb25zKSl7CiAgdHJlZVBhc3RlPC1nc3ViKHBhc3RlKCIoIixuYW1lcyhleHBhbnNpb25zKVtpXSwiOikiLHNlcD0iIikscGFzdGUoIigiLHBhc3RlKHJlcCgiXFwxIixleHBhbnNpb25zW2ldKSxjb2xsYXBzZT0iOjAsIixzZXA9IiIpLCI6MCk6IixzZXA9IiIpLHRyZWVQYXN0ZSkKfQoKIyBsb2FkIG1hbmlwdWxhdGVkIHRyZWUgYXMgdHJlZSBvYmplY3QKcmF3LnRyZWU8LXJlYWQudHJlZSh0ZXh0PXRyZWVQYXN0ZSkKcmF3LnRyZWUkdGlwLmxhYmVsPC1tYWtlX3VuaXF1ZShyYXcudHJlZSR0aXAubGFiZWwsc2VwPSItc3AiKQpgYGAKCgpgYGB7cn0KIyBzaG93IGxhYmVscyBmb3IgZmlndXJpbmcgb3V0IHN1YmZhbWlsaWVzCnBsb3QocmF3LnRyZWUsc2hvdy50aXAubGFiZWwgPSBGKQpub2RlbGFiZWxzKGZyYW1lID0gICJub25lIikKYGBgCgoKYGBge3J9CiMgc3Vic2V0IHRvIGV4dGFudCBhbnRzLCBNYXJ0aWFsaXMgaGFzIHRvIGJlIG5vZGUgMQp0cmVlPC1TdWJ0cmVlKFByZW9yZGVyKHJhdy50cmVlKSwgNDI3KQpncmVwKCJNYXJ0aWFsaXMiLHRyZWUkdGlwLmxhYmVsKQp0cmVlJHRpcC5sYWJlbDwtZ3N1Yigic29tZSguKlsyLTldKXxzb21lKC4qKTEiLCJcXDFcXDIiLHRyZWUkdGlwLmxhYmVsKQpgYGAKCmBgYHtyfQojIHBsb3Qgd2l0aCBzdWJmYW1pbGllcwoKcDwtZ2d0cmVlKHRyZWUsc2l6ZT0uMSkreGxpbSgwLDQwKStnZW9tX3RpcGxhYihhbGlnbj1ULHNpemU9MSkrIGdlb21fdGV4dChhZXMobGFiZWw9bm9kZSksc2l6ZT0xLGNvbD0iZGFya3JlZCIpCmBgYAoKCgoKYGBge2Jhc2h9CiMgRG93bmxvYWQgc3BlY2llcyBjb3VudHMgZm9yIGFsbCBnZW5lcmEgYW5kIG90aGVyIGluZm8gZnJvbSBhbnR3aWtpCiNjdXJsIGh0dHBzOi8vd3d3LmFudHdpa2kub3JnL3dpa2kvaW1hZ2VzL2EvYWQvQW50V2lraV9WYWxpZF9HZW5lcmEudHh0IHwgTENfQUxMPUMgc2VkICdzL1teWzpibGFuazpdWzpwcmludDpdXS8vZycgID4gLi4vbWlzYy9kYXRhL0FudFdpa2kuY2xlYW4udHN2CmBgYApgYGB7cn0Kc3BlY2llc2NvdW50PC1yZWFkLmNzdigiLi4vLi4vbWlzYy9kYXRhL0FudFdpa2kuY2xlYW4udHN2IixzZXA9Ilx0IikKc3BlY2llc2NvdW50JFRyaWJlW3NwZWNpZXNjb3VudCRUcmliZT09IiJdPC1wYXN0ZSgidHIuICIsc3BlY2llc2NvdW50JFN1YmZhbWlseVtzcGVjaWVzY291bnQkVHJpYmU9PSIiXSxzZXA9IiIpCmBgYApgYGB7cn0KZGF0YU1lcmdlPC1tZXJnZShkYXRhLHNwZWNpZXNjb3VudCxieS54PSJnZW51cyIsYnkueT0iVGF4b25OYW1lIixhbGwueD1ULGFsbC55PVQpCgpkYXRhTWVyZ2UgPC0gZGF0YU1lcmdlICU+JQogIHNlbGVjdCh0YXhJRCwgZXZlcnl0aGluZygpKQoKIyBnaXZlIG5hbWUgdG8gYWxsIHVuc2VxdWVuY2VkIGdlbmVyYQpkYXRhTWVyZ2UkdGF4SURbaXMubmEoZGF0YU1lcmdlJHRheElEKV08LWRhdGFNZXJnZSRnZW51c1tpcy5uYShkYXRhTWVyZ2UkdGF4SUQpXQojIHNldCBzcGVjaWVzIENvdW50IHRvIE5BIGZvciBhbGwgbXVsdGlwbGUgZ2VuZXJhIHNwZWNpZXMKZGF0YU1lcmdlJFNwZWNpZXNDb3VudFtncmVwKCItc3BbMi05XSIsZGF0YU1lcmdlJHRheElELHZhbHVlID0gRildPC1OQQpgYGAKCgogYWRkIGRhdGEgdG8gdHJlZQpgYGB7cn0KcDwtZ2d0cmVlKHRyZWUsc2l6ZT0uMixsYXlvdXQgPSAiY2lyY3VsYXIiKSt4bGltKDAsNDApCiNwJGRhdGEkbGFiZWw8LW1ha2VfdW5pcXVlKHAkZGF0YSRsYWJlbCxzZXAgPSAiLXNwIikKI3AkZGF0YSRsYWJlbFtncmVwKCJOQS4iLHAkZGF0YSRsYWJlbCxpZ25vcmUuY2FzZSA9IEYpXTwtTkEKZGF0YU1lcmdlJERpc3BsYXlOYW1lPC1nc3ViKCIgXFwoLioiLCIiLGRhdGFNZXJnZSRTcGVjaWVzLi5mcm9tLkFzc2VtYmx5Lk92ZXJ2aWV3LikKcDI8LXAgJTwrJSBkYXRhTWVyZ2UgCmBgYAoKCgpgYGB7cn0KCmFudEdlbmVyYS50cmVlIDwtIHAyICsKICAgICAgICAgIGdlb21fdGlwbGFiKGFsaWduPVQsc2l6ZT0xLjIsb2Zmc2V0PS41LGxpbmVzaXplPTApKwogICAgICAgICAgZ2VvbV90aXBwb2ludChhZXMoc2l6ZSA9IFNwZWNpZXNDb3VudCxmaWxsPVRyaWJlKSxwY2g9MjEsYWxwaGE9LjUpICsgCiAgICAgICAgICBndWlkZXMoZmlsbD0ibm9uZSIsY29sb3I9Im5vbmUiKSsKICAgICAgICAgIHRoZW1lX3RyZWUoKSsKICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoLjksLjg1KSxsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKCmFudEdlbmVyYS50cmVlLncuR0FHQSA8LSBwMiArCiAgICAgICAgICBnZW9tX3RpcGxhYihhZXMobGFiZWw9RGlzcGxheU5hbWUsY29sPVRyaWJlLGFscGhhPWlmZWxzZShpcy5uYShEaXNwbGF5TmFtZSksMCwxKSksYWxpZ249VCxzaXplPTEuOCxvZmZzZXQgPSA4LGxpbmVzaXplID0gMCkrCiAgICAgICAgICBnZW9tX3RpcGxhYihhbGlnbj1ULHNpemU9MS4yLG9mZnNldD0uNSxsaW5lc2l6ZT0wKSsKICAgICAgICAgIGdlb21fdGlwcG9pbnQoYWVzKHNpemUgPSBTcGVjaWVzQ291bnQsZmlsbD1UcmliZSkscGNoPTIxLGFscGhhPS41KSArIAogICAgICAgICAgZ3VpZGVzKGZpbGw9Im5vbmUiLGNvbG9yPSJub25lIixhbHBoYT0ibm9uZSIpKwogICAgICAgICAgdGhlbWVfdHJlZSgpKwogICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYyguOSwuODUpLGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQoKYGBgCgpgYGB7cn0KYW50R2VuZXJhLnRyZWUKZ2dzYXZlKCIuLi8uLi9taXNjL3Bsb3RzL2FudFRyZWUucGRmIixoZWlnaHQ9MTApCgphbnRHZW5lcmEudHJlZS53LkdBR0EKZ2dzYXZlKCIuLi8uLi9taXNjL3Bsb3RzL2FudFRyZWUuR0FHQS5wZGYiLGhlaWdodD0xMCkKIyBIb3cgbWFueSBHQUdBIHNwZWNpZXMgYXJlIHNob3duPwpzdW0oIWlzLm5hKGRhdGFNZXJnZSREaXNwbGF5TmFtZSkpCmBgYAoKCgpgYGB7cn0KR0FHQS50cmVlPC1rZWVwLnRpcCh0cmVlLGFudEdlbmVyYS50cmVlLncuR0FHQSRkYXRhJG5vZGVbIWlzLm5hKGFudEdlbmVyYS50cmVlLncuR0FHQSRkYXRhJERpc3BsYXlOYW1lKV0pCgpHQUdBLnRyZWUucGxvdDwtZ2d0cmVlKEdBR0EudHJlZSxzaXplPS4yKSAlPCslIGRhdGFNZXJnZSAKR0FHQS50cmVlLnBsb3QyPC1HQUdBLnRyZWUucGxvdCtnZW9tX3RpcGxhYihhZXMobGFiZWw9RGlzcGxheU5hbWUsY29sPWlmZWxzZShncmVwbCgiR0FHQSIsRU5UUlkuRlJPTS5BU1NFTUJMWS5PVkVSVklFVyksIkdBR0EiLCJvdGhlciIpKSxzaXplPTEuNSxzaG93LmxlZ2VuZD1GKStzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iIix2YWx1ZXMgPSBjKCJibGFjayIsImdyZXk2MCIpKQpgYGAKCmBgYHtyfQpHQUdBLnRyZWUucGxvdDIKYGBgCgoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCiMgQWRkIHRyaWJlIGluZm8gYXMgY2xhZGUgaGlnaGxpZ2h0cwoKR0FHQS50cmVlLnBsb3QyJGRhdGEkVHJpYmU8LWFzLmZhY3RvcihHQUdBLnRyZWUucGxvdDIkZGF0YSRUcmliZSkKVHJpYmVOb2RlczwtdmVjdG9yKCkKCnRyaWJlczwtbGV2ZWxzKEdBR0EudHJlZS5wbG90MiRkYXRhJFRyaWJlKQp0cmliZUNvdW50PC10YWJsZShHQUdBLnRyZWUucGxvdDIkZGF0YSRUcmliZSkKCiMgbG9vcCBvdmVyIGFsbCB0cmliZXMgYW5kIGV4dHJhY3QgdGhlIE1SQ0Egbm9kZSBvZiBhbGwgZ2VuZXJhIGZvciBlYWNoIHRyaWJlCmZvciAoaSBpbiAxOmxlbmd0aCh0cmliZXMpKXsKICBub2RlczwtdW5saXN0KHN1YnNldChHQUdBLnRyZWUucGxvdDIkZGF0YSxUcmliZT09dHJpYmVzW2ldLG5vZGUsaW5jbHVkZS5zZWxmPVQpKQogIGlmIChsZW5ndGgobm9kZXMpPT0xKXsKICAgIFRyaWJlTm9kZXNbaV08LW5vZGVzCiAgfWVsc2V7CiAgICBUcmliZU5vZGVzW2ldPC1tcmNhLnBoeWxvKEdBR0EudHJlZSxub2RlcykKICB9Cn0KCiNkZWZpbmUgY2xhZGUgY29sb3JzCnNldC5zZWVkKDEpCmNsYWRlQ29sb3I8LWRpc3RpbmN0Q29sb3JQYWxldHRlKGxlbmd0aChUcmliZU5vZGVzKSkKIApuYW1lcyhUcmliZU5vZGVzKTwtdHJpYmVzCm5hbWVzKGNsYWRlQ29sb3IpPC10cmliZXMKdHJpYmVjb2xvcnM8LWRhdGEuZnJhbWUodHJpYmU9bmFtZXMoY2xhZGVDb2xvciksY29sb3I9Y2xhZGVDb2xvcikKI3dyaXRlLnRhYmxlKHRyaWJlY29sb3JzLGZpbGU9Ii4uLy4uL21pc2MvZGF0YS90cmliZUNvbG9ycy50c3YiLHNlcD0iXHQiLHF1b3RlPUYsIHJvdy5uYW1lcz1GKQpHQUdBLnRyZWUucGxvdDM8LUdBR0EudHJlZS5wbG90MgoKIyBsb29wIG92ZXIgZWFjaCB0cmliZSBhbmQgYWRkIGEgY2xhZGUgaGlnaGxpZ2h0LiBMYWJlbCB0aGUgaGlnaGxpZ2h0IG9ubHkgZm9yIGxhcmdlciBjbGFkZXMKZm9yICh0cmliZSBpbiB0cmliZXMpewogIGlmICh0cmliZUNvdW50W3RyaWJlXT40KXsKICAgIEdBR0EudHJlZS5wbG90MzwtR0FHQS50cmVlLnBsb3QzK2dlb21fY2xhZGVsYWJlbChub2RlPVRyaWJlTm9kZXNbdHJpYmVdLGxhYmVsPSB0cmliZSxhbGlnbiA9IFQsIG9mZnNldCA9IDUsIGNvbD1jKGNsYWRlQ29sb3JbdHJpYmVdLCJibGFjayIpLGJhcnNpemUgPSAxLGFuZ2xlID0gOTAsZm9udHNpemUgPSAyLG9mZnNldC50ZXh0ID0gLjUsaGp1c3QgPSAuNSkreGxpbV90cmVlKDI1KQogIH1lbHNlewogICAgR0FHQS50cmVlLnBsb3QzPC1HQUdBLnRyZWUucGxvdDMrZ2VvbV9jbGFkZWxhYmVsKG5vZGU9VHJpYmVOb2Rlc1t0cmliZV0sbGFiZWw9ICIiLGFsaWduID0gVCwgb2Zmc2V0ID0gNSwgY29sPWNsYWRlQ29sb3JbdHJpYmVdLGJhcnNpemUgPSAxKSt4bGltX3RyZWUoMzApCiAgfQp9CgpgYGAKCmBgYHtyfQpHQUdBLnRyZWUucGxvdDMKYGBgCgpgYGB7cn0KIyBleHRyYWN0IGRhdGEgZnJvbSB0cmVlIGZvciBmYWNldCBwbG90dGluZwoKZmFjZXREYXRhPC1hcy5kYXRhLmZyYW1lKEdBR0EudHJlZS5wbG90MiRkYXRhW0dBR0EudHJlZS5wbG90MiRkYXRhJGlzVGlwPT1ULF0pCmZhY2V0RGF0YSRpZDwtZmFjZXREYXRhJGxhYmVsCmZhY2V0RGF0YSRHZW5vbWUuc2l6ZTwtYXMubnVtZXJpYyhmYWNldERhdGEkR2Vub21lLnNpemUpCmZhY2V0RGF0YSRTY2FmZm9sZC5ONTA8LWFzLm51bWVyaWMoZmFjZXREYXRhJFNjYWZmb2xkLk41MCkKZmFjZXREYXRhJENvbnRpZy5ONTA8LWFzLm51bWVyaWMoZmFjZXREYXRhJENvbnRpZy5ONTApCmZhY2V0RGF0YSROdW1iZXIub2Yuc2NhZmZvbGRzPC1hcy5udW1lcmljKGZhY2V0RGF0YSROdW1iZXIub2Yuc2NhZmZvbGRzKQoKIyBleHRyYWN0IEJVU0NPIHNjb3JlcwpmYWNldERhdGEkQlVTQ08uaHltLkM8LWFzLm51bWVyaWMoZ3N1YigiQzooLio/KSUuKiIsIlxcMSIsZmFjZXREYXRhJEJVU0NPLkh5bWVub3B0ZXJhKSkKZmFjZXREYXRhJEJVU0NPLmh5bS5GPC1hcy5udW1lcmljKGdzdWIoIi4qRjooLio/KSUuKiIsIlxcMSIsZmFjZXREYXRhJEJVU0NPLkh5bWVub3B0ZXJhKSkKZmFjZXREYXRhJEJVU0NPLmh5bS5NPC1hcy5udW1lcmljKGdzdWIoIi4qTTooLio/KSUuKiIsIlxcMSIsZmFjZXREYXRhJEJVU0NPLkh5bWVub3B0ZXJhKSkKCmZhY2V0RGF0YSRCVVNDTy5ldWsuQzwtYXMubnVtZXJpYyhnc3ViKCJDOiguKj8pJS4qIiwiXFwxIixmYWNldERhdGEkQlVTQ08uRXVrYXJ5b3RhKSkKZmFjZXREYXRhJEJVU0NPLmV1ay5GPC1hcy5udW1lcmljKGdzdWIoIi4qRjooLio/KSUuKiIsIlxcMSIsZmFjZXREYXRhJEJVU0NPLkV1a2FyeW90YSkpCmZhY2V0RGF0YSRCVVNDTy5ldWsuTTwtYXMubnVtZXJpYyhnc3ViKCIuKk06KC4qPyklLioiLCJcXDEiLGZhY2V0RGF0YSRCVVNDTy5FdWthcnlvdGEpKQoKCmRkIDwtIGRhdGEuZnJhbWUoaWQ9ZmFjZXREYXRhJGxhYmVsLCBHUz1yb3VuZChmYWNldERhdGEkR2Vub21lLnNpemUvMTAwMDAwMCksTjUwPWZhY2V0RGF0YSRTY2FmZm9sZC5ONTAsbnNjZj1mYWNldERhdGEkTnVtYmVyLm9mLnNjYWZmb2xkcyxCLmh5bS5DPWZhY2V0RGF0YSRCVVNDTy5oeW0uQyxCLmh5bS5GPWZhY2V0RGF0YSRCVVNDTy5oeW0uRixCLmh5bS5NPWZhY2V0RGF0YSRCVVNDTy5oeW0uTSxCLmV1ay5DPWZhY2V0RGF0YSRCVVNDTy5ldWsuQyxCLmV1ay5GPWZhY2V0RGF0YSRCVVNDTy5ldWsuRixCLmV1ay5NPWZhY2V0RGF0YSRCVVNDTy5ldWsuTSx0cmliZT1mYWNldERhdGEkVHJpYmUsUGFjQmlvPWZhY2V0RGF0YSRQYWNCaW8uMSxzdExGUj1mYWNldERhdGEkc3RMRlIuMixIaUM9ZmFjZXREYXRhJEhpLkMuMSxSTkFzZXE9ZmFjZXREYXRhJFJOQS4uYWRkaXRpb25hbC5zYW1wbGVzLnN0aWxsLnVuZGVyLnNlcS4uLEZpbmFsaXplZD1mYWNldERhdGEkQ3VycmVudC5zdGF0ZSx3Z3M9ZmFjZXREYXRhJFNob3J0LnJlYWRzLi5XR1MuLjEsR2lkPWZhY2V0RGF0YSRHQUdBLklELHN0YXR1cz1mYWNldERhdGEkQ3VycmVudC5zdGF0ZSkKZGQkdHJpYmVDb2xvciA8LSBjbGFkZUNvbG9yW21hdGNoKGRkJHRyaWJlLCBuYW1lcyhjbGFkZUNvbG9yKSldCgpkZCRQYWNCaW9bIWlzLm5hKGRkJFBhY0JpbyldPC0xCmRkJFBhY0Jpb1tpcy5uYShkZCRQYWNCaW8pXTwtMApkZCRzdExGUlshaXMubmEoZGQkc3RMRlIpXTwtMQpkZCRzdExGUltpcy5uYShkZCRzdExGUildPC0wCmRkJEhpQ1shaXMubmEoZGQkSGlDKV08LTEKZGQkSGlDW2lzLm5hKGRkJEhpQyldPC0wCmRkJHdnc1shaXMubmEoZGQkd2dzKV08LTEKZGQkd2dzW2lzLm5hKGRkJHdncyldPC0wCgpyb3duYW1lcyhkZCk8LWRkJGlkCgpkZCRSTkFzZXFbIWlzLm5hKGRkJFJOQXNlcSldPC0zCmRkJFJOQXNlcVtpcy5uYShkZCRSTkFzZXEpXTwtMAoKZGQkRmluYWxpemVkW2dyZXAoIkZpbmFsIixkZCRGaW5hbGl6ZWQpXTwtMgpkZCRGaW5hbGl6ZWRbZGQkRmluYWxpemVkIT0yXTwtMAoKZGQkRGlzcGxheU5hbWU8LWZhY2V0RGF0YSREaXNwbGF5TmFtZQoKZGQkbm9kZTwtZmFjZXREYXRhJG5vZGUKZGQkdGVjaDwtYXMuZmFjdG9yKHBhc3RlKGRkJFBhY0JpbyxkZCRIaUMsZGQkc3RMRlIsc2VwPSIiKSkKbGV2ZWxzKGRkJHRlY2gpW2xldmVscyhkZCR0ZWNoKT09IjAwMCJdPC0ibm9uLUdBR0EiCmxldmVscyhkZCR0ZWNoKVtsZXZlbHMoZGQkdGVjaCk9PSIxMDAiXTwtIlBhY0JpbyIKbGV2ZWxzKGRkJHRlY2gpW2xldmVscyhkZCR0ZWNoKT09IjAwMSJdPC0ic3RMRlIiCmxldmVscyhkZCR0ZWNoKVtsZXZlbHMoZGQkdGVjaCk9PSIxMDEiXTwtIlAvcyIKbGV2ZWxzKGRkJHRlY2gpW2xldmVscyhkZCR0ZWNoKT09IjExMCJdPC0iUC9IIgpsZXZlbHMoZGQkdGVjaClbbGV2ZWxzKGRkJHRlY2gpPT0iMTExIl08LSJQL0gvcyIKCgpkZEhlYXRtYXA8LWRkW2MoImlkIiwidHJpYmVDb2xvciIsInRyaWJlIiwiUGFjQmlvIiwic3RMRlIiLCJIaUMiLCJ3Z3MiLCJGaW5hbGl6ZWQiLCJSTkFzZXEiKV0KCiNHQUdBLnRyZWUucGxvdDQ8LWZhY2V0X3Bsb3QoR0FHQS50cmVlLnBsb3QzLCAnR2Vub21lIFNpemUnLCBkYXRhID0gZGQsIGdlb209Z2VvbV9iYXJoLCBtYXBwaW5nID0gYWVzKHggPSBHUyxmaWxsPXRyaWJlQ29sb3IpLCAKICMgICAgICAgICAgICAgICBzdGF0PSdpZGVudGl0eScgKSsgdGhlbWVfdHJlZTIoKSArIHNjYWxlX2ZpbGxfaWRlbnRpdHkoKQpgYGAKCgpgYGB7cn0KR0FHQS50cmVlLnBsb3QuaGVhdG1hcDwtZ2hlYXRtYXAoR0FHQS50cmVlLnBsb3QzK3lsaW0oMCwxNjUpLCBkZEhlYXRtYXBbLGMoNDo5KV0sIG9mZnNldD03LCBjb2xuYW1lcz1ULCBsZWdlbmRfdGl0bGU9IlRlY2giLCAgd2lkdGggPSAuMjUsY29sbmFtZXNfcG9zaXRpb24gPSAidG9wIiwgY29sbmFtZXNfb2Zmc2V0X3kgPSAxLGNvbG5hbWVzX2FuZ2xlPTY1LGhqdXN0PTAsZm9udC5zaXplID0gMi44KSsKICAgICAgICAgICAgICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZ3JleTkwIiwibGlnaHRncmVlbiIsImRhcmtyZWQiLCJsaWdodGJsdWUiKSkrCiAgICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgpgYGAKYGBge3J9CkdBR0EudHJlZS5wbG90LmhlYXRtYXAKZ2dzYXZlKCIuLi8uLi9taXNjL3Bsb3RzL0dBR0EudHJlZS50ZWNobmlxdWUucGRmIixHQUdBLnRyZWUucGxvdC5oZWF0bWFwLHdpZHRoPTYsaGVpZ2h0PTEwKQpgYGAKCmBgYHtyfQpkZEhlYXRtYXAyPC1kZFtjKCJpZCIsInRyaWJlQ29sb3IiLCJ0cmliZSIsIkIuaHltLkMiLCJCLmV1ay5DIildCmNvbG5hbWVzKGRkSGVhdG1hcDIpWzQ6NV08LWMoIkh5bWVub3B0LiIsIkV1a2FyeW90YSIpCnJvdy5uYW1lcyhkZEhlYXRtYXAyKTwtZGRIZWF0bWFwMiRpZApgYGAKCmBgYHtyfQpoZWF0Y29sIDwtIGMoInN0ZWVsYmx1ZSIsImN5YW4iLCAicmVkIikKR0FHQS50cmVlLnBsb3QuYnVzY288LWdoZWF0bWFwKEdBR0EudHJlZS5wbG90Myt5bGltKDAsMTY1KSwgZGRIZWF0bWFwMlssYyg0OjUpXSwgb2Zmc2V0PTcsIGNvbG5hbWVzPVQsIGxlZ2VuZF90aXRsZT0iVGVjaCIsICB3aWR0aCA9IC4yMTUsY29sbmFtZXNfcG9zaXRpb24gPSAidG9wIiwgY29sbmFtZXNfb2Zmc2V0X3kgPSAxLGNvbG5hbWVzX2FuZ2xlPTY1LGhqdXN0PTAsZm9udC5zaXplID0gMi41KStzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gcmV2KGhlYXRjb2wpLG5hLnZhbHVlID0gImdyZXk5MCIsbmFtZT0iJSBjb21wLiBCdXNjbyIpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoLjIsLjgpLGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQpgYGAKYGBge3J9CkdBR0EudHJlZS5wbG90LmJ1c2NvCmdnc2F2ZSgiLi4vLi4vbWlzYy9wbG90cy9HQUdBLnRyZWUuYnVzY28ucGRmIixHQUdBLnRyZWUucGxvdC5idXNjbyx3aWR0aD02LGhlaWdodD0xMCkKYGBgCgoKYGBge3J9CgpHQUdBLnRyZWUucGxvdC5ONTA8LWZhY2V0X3Bsb3QoR0FHQS50cmVlLnBsb3QuaGVhdG1hcCtuZXdfc2NhbGUoImNvbG9yIiksICdzY2ZONTAgKE1iKScsIGRhdGEgPSBkZCwgZ2VvbT1nZW9tX3BvaW50LCBtYXBwaW5nID0gYWVzKHggPSBONTAvMTAwMDAwMCxjb2xvcj10ZWNoKSxzaXplPTIpKyAKICB0aGVtZV90cmVlMigpKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiICxndWlkZSA9IGd1aWRlX2xlZ2VuZChucm93PTIsbGFiZWwucG9zaXRpb24gPSAidG9wIikpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoLjksLjkpLAogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yPSJibGFjayIsZmlsbD1yZ2IoMCwwLDAsMCkpLAogICAgICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oMCwyLDIsMiwibW0iKSwKICAgICAgICBsZWdlbmQuc3BhY2luZyA9IHVuaXQoMCwibW0iKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAsIm1tIiksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMCwibW0iKSwKICAgICAgICBsZWdlbmQuYm94LnNwYWNpbmcgPSB1bml0KDAsIm1tIikpKwogICAgICAgIGd1aWRlcyhmaWxsPSJub25lIikrCiAgeWxpbSgwLDE3MCkKCmBgYApgYGB7cn0KR0FHQS50cmVlLnBsb3QuTjUwCmBgYAoKCmBgYHtyfQojIGNoYW5nZSByZWxfd2lkdGggb2YgcGFuZWxzCiMjIHNlZSBodHRwczovL2dyb3Vwcy5nb29nbGUuY29tL2cvYmlvYy1nZ3RyZWUvYy90WjBxa2x1QmVHVS9tL25rZldDOWl4Q3dBSgoKZ3QgPSBnZ3Bsb3RfZ3RhYmxlKGdncGxvdF9idWlsZChHQUdBLnRyZWUucGxvdC5ONTApKQojZ3RhYmxlX3Nob3dfbGF5b3V0KGd0KSAjIHdpbGwgc2hvdyB5b3UgdGhlIGxheW91dCAtIHZlcnkgaGFuZHkgZnVuY3Rpb24KI2d0ICMgc2VlIHBsb3QgbGF5b3V0IGluIHRhYmxlIGZvcm1hdApndCRsYXlvdXQkbFtncmVwKCdwYW5lbC0xJywgZ3QkbGF5b3V0JG5hbWUpXSAjIHlvdSB3YW50IHRvIGZpbmQgdGhlIGNvbHVtbiBzcGVjaWZpYyB0byBwYW5lbC0yCmd0JHdpZHRoc1s1XSA9IDEuOSpndCR3aWR0aHNbNV0gIyBpbiB0aGlzIGNhc2UgaXQgd2FzIGNvbG11biA3IC0gcmVkdWNlIHRoZSB3aWR0aCBieSBhIGhhbGYKI2dyaWQuZHJhdyhndCkgIyBwbG90IHdpdGggZ3JpZCBkcmF3CmBgYAoKCmBgYHtyfQpncmlkLmRyYXcoZ3QpCmdnc2F2ZSgiLi4vLi4vbWlzYy9wbG90cy9HQUdBLnRyZWUubjUwLnBkZiIsZ3JpZC5kcmF3KGd0KSx3aWR0aD0xMCxoZWlnaHQ9OSkKCmBgYAoKCgoKYGBge1IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KZGQkUk5Bc2VxPC1hcy5mYWN0b3IoZGQkUk5Bc2VxKQpkZCRONTBtYjwtcm91bmQoZGQkTjUwLzEwMDAwMDAsMikKbGV2ZWxzKGRkJFJOQXNlcSk8LWMoIk4iLCJZIikKYGBgCgoKYGBge1IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KYXNzZW1ibHkucXVhbGl0eS5wbG90MTwtIGdncGxvdChkZCkgKyAKICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChhZXMoeD1uc2NmLHk9TjUwbWIsc2l6ZT1HUyxmaWxsPXRlY2gsY29sPVJOQXNlcSxzcGVjaWVzPURpc3BsYXlOYW1lKSxzdHJva2U9LjkscGNoPTIxLGFscGhhPS41KSArCiAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lX2NsYXNzaWMoKSArIAogICAgICAgICAgICAgICAgICAgICAgICBzY2FsZV94X2xvZzEwKGxhYmVscz1jKCIxMCIsIjEwMCIsIjFrIiwiMTBrIiwiMjBrIiksYnJlYWtzPWMoMTAsMTAwLDEwMDAsMTAwMDAsMjAwMDAwKSkgKyAKICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKHJnYigxLDEsMSwwKSwicmVkIikpCgphc3NlbWJseS5xdWFsaXR5LnBsb3QyPC1hc3NlbWJseS5xdWFsaXR5LnBsb3QxKyAKICAgICAgICAgICAgICAgICAgICAgICAgeGxhYigiIyBzY2FmZm9sZHMiKSArIAogICAgICAgICAgICAgICAgICAgICAgICB5bGFiKCJzY2FmZm9sZCBONTAgKE1iKSIpKwogICAgICAgICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKC44LCAuNjUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnNwYWNpbmcgPSB1bml0KC4xLCAnY20nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuMywiY20iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSAyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3guYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLGZpbGw9TkEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSAgZWxlbWVudF9yZWN0KGNvbG91ciA9IE5BLCBmaWxsID0gTkEpKQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uLy4uL21pc2MvcGxvdHMvR0FHQS5hc3NlbWJseVF1YWxpdHkucGRmIixhc3NlbWJseS5xdWFsaXR5LnBsb3QyLHdpZHRoPTYsaGVpZ2h0PTYpCmBgYAoKYGBge1IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KcDEucmF3PC1nZ3Bsb3RseShhc3NlbWJseS5xdWFsaXR5LnBsb3QyLHRvb2x0aXAgPSBjKCJONTBtYiIsIkRpc3BsYXlOYW1lIikpICU+JWxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB4ID0gMCwgeSA9LTAuMSkpCnAxPC1nZ3Bsb3RseShhc3NlbWJseS5xdWFsaXR5LnBsb3QyLHRvb2x0aXAgPSBjKCJONTBtYiIsIkRpc3BsYXlOYW1lIikpICU+JWxheW91dChsZWdlbmQgPSBsaXN0KG9yaWVudGF0aW9uID0gImgiLCB4ID0gMCwgeSA9LTAuMSkpCgojIGNoYW5nZSBsZWdlbmQgaW5mbyBmb3IgcGxvdGx5IHBsb3QKZm9yIChpIGluIDE6bGVuZ3RoKHAxJHgkZGF0YSkpewogIHAxJHgkZGF0YVtbaV1dJGxlZ2VuZGdyb3VwPC1nc3ViKCIuKiIsIiIscDEkeCRkYXRhW1tpXV0kbGVnZW5kZ3JvdXApCiAgcDEkeCRkYXRhW1tpXV0kbmFtZTwtZ3N1YigiXFwofFxcKSIsIiIscDEkeCRkYXRhW1tpXV0kbmFtZSkKfQoKc2F2ZVdpZGdldChhc193aWRnZXQocDEpLCAiLi4vLi4vbWlzYy9wbG90cy9HQUdBLmFzc2VtYmx5UXVhbGl0eS5odG1sIikKCmBgYAoKYGBge1IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KI3ByZXBhcmUgYm94cGxvdHMgYW5kIGNhbGN1bGF0ZSBiZXN0L3dvcnN0IG1ldHJpY3MKR0FHQS5xdWFsaXR5LmJveHBsb3RzPC1nZ3Bsb3QoZGQsYWVzKHk9TjUwbWIseD10ZWNoLGZpbGw9dGVjaCkpK2dlb21fYm94cGxvdChvdXRsaWVyLmNvbG91ciA9IE5BKStnZW9tX2ppdHRlcihhbHBoYT0uMix3aWR0aCA9IDAuMikrc2NhbGVfeV9sb2cxMCgpK3RoZW1lX2NsYXNzaWMoKSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKC44LCAuMiksYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKSt4bGFiKCIiKSt5bGFiKCJzY2FmZm9sZCBONTAgKE1iKSIpCmBgYApgYGB7cn0KZ2dzYXZlKCIuLi8uLi9taXNjL3Bsb3RzL0dBR0EuYXNzZW1ibHlRdWFsaXR5LmJveHBsb3RzLnBkZiIsR0FHQS5xdWFsaXR5LmJveHBsb3RzLHdpZHRoPTYsaGVpZ2h0PTYpCmBgYAoKYGBge3J9CmRkJFN1YmZhbWlseTwtZmFjZXREYXRhJFN1YmZhbWlseS55CmRkJERpc3BsYXlOYW1lVTwtbWFrZV91bmlxdWUoZGQkRGlzcGxheU5hbWUsc2VwPSIiKQpkZCREaXNwbGF5TmFtZVU8LSBmYWN0b3IoZGQkRGlzcGxheU5hbWVVLCBsZXZlbHMgPSBkZCREaXNwbGF5TmFtZVVbcmV2KG9yZGVyKGRkJFN1YmZhbWlseSxkZCRHUykpXSkKYGBgCgoKYGBge1IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KI2NvbXB1dGUgZ2Vub21lIHNpemUgcGxvdApkZC50bXA8LWRkCmRkLnRtcCRTdWJmYW1pbHlbZGQudG1wJFN1YmZhbWlseT09IlBzZXVkb215cm1lY2luYWUiXTwtIlBzZXVkb215cm0uIgpHQUdBLkdlbm9tZVNpemU8LWdncGxvdChzdWJzZXQoZGQudG1wLCFpcy5uYShHUykpLGFlcyh4PURpc3BsYXlOYW1lVSx5PUdTKSkKCkdBR0EuR2Vub21lU2l6ZTI8LUdBR0EuR2Vub21lU2l6ZStnZW9tX2NvbChhZXMoZmlsbD1TdWJmYW1pbHkpLGNvbD0xLHNpemU9LjIpICsgCiAgY29vcmRfZmxpcCgpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoIlN1YmZhbWlseSIpICsgCiAgeWxhYigiR2Vub21lIFNpemUgKE1iKSIpKwogIHhsYWIoIlNwZWNpZXMiKSsKICB0aGVtZV9jbGFzc2ljKCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9Ni41KSkrCiAgZ2VvbV9wb2ludChhZXMoY29sPUIuaHltLkMpLHNpemU9MS41KSsKICBndWlkZXMoY29sID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxLCB0aXRsZS5wb3NpdGlvbiA9ICJ0b3AiLHRpdGxlID0gIkh5bS4gQlVTQ08gJSIpLAogICAgICAgIGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDQsIHRpdGxlLnBvc2l0aW9uID0gInRvcCIpKSsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIpKQoKYGBgCgpgYGB7UiBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpnZ3NhdmUoIi4uLy4uL21pc2MvcGxvdHMvR0FHQS5nZW5vbWVzaXplLnBkZiIsR0FHQS5HZW5vbWVTaXplMix3aWR0aD04LGhlaWdodD0xNSkKYGBgCg==